#include "D3D9_PCH.h"
#include "ParticleCluster.h"

D3DVERTEXELEMENT9 CParticleCluster::s_VertexDecl[] = {
	{ 0, 0,     D3DDECLTYPE_FLOAT3,     D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_POSITION,  0 },
    { 0, 3 * 4, D3DDECLTYPE_FLOAT2,     D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_TEXCOORD,  0 },
    { 1, 0,     D3DDECLTYPE_D3DCOLOR,   D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_COLOR,     0 },
    { 1, 4,     D3DDECLTYPE_FLOAT4,		D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_POSITION,  1 },
	{ 1, 20,	D3DDECLTYPE_FLOAT1,		D3DDECLMETHOD_DEFAULT,	D3DDECLUSAGE_PSIZE,		0 },
    D3DDECL_END()
};

CParticleCluster::CParticleCluster( int numMaxInstance )
	: m_pGeometry( NULL )
	, m_pInstanceBuffer( NULL )
	, c_numMaxInstance( numMaxInstance )
	, m_numCurInstance( 0 )
{
	m_pGeometry = _CreateBillboardMesh();
	g_pDevice->CreateVertexBuffer( numMaxInstance * sizeof(INSTANCE_DATA), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_pInstanceBuffer, NULL );
	
	g_pDevice->CreateVertexDeclaration( s_VertexDecl, &m_pVDecl );
}


CParticleCluster::~CParticleCluster(void)
{
	SAFE_RELEASE( m_pGeometry );
	SAFE_RELEASE( m_pInstanceBuffer );
	SAFE_RELEASE( m_pVDecl );
}

void CParticleCluster::UpdateCluster( const INSTANCE_DATA* pFillData, int numData ) {

	assert( (numData <= c_numMaxInstance) && "Too Many Instance" );
	
	m_numCurInstance = (numData > c_numMaxInstance) ? c_numMaxInstance : numData;

	INSTANCE_DATA* pInstB;
	m_pInstanceBuffer->Lock( 0, 0, (void**)&pInstB, D3DLOCK_DISCARD );
		memcpy( pInstB, pFillData, sizeof(INSTANCE_DATA) * m_numCurInstance );
	m_pInstanceBuffer->Unlock();
}

void CParticleCluster::PreRender() const {
	IDirect3DVertexBuffer9* VB;
	IDirect3DIndexBuffer9*	IB;
	m_pGeometry->GetVertexBuffer( &VB );
	m_pGeometry->GetIndexBuffer( &IB );

	// ser vertex declaration
	g_pDevice->SetVertexDeclaration( m_pVDecl );
	// set geometry
	g_pDevice->SetStreamSourceFreq( 0, D3DSTREAMSOURCE_INDEXEDDATA | m_numCurInstance );
	g_pDevice->SetStreamSource( 0, VB, 0, D3DXGetDeclVertexSize( s_VertexDecl, 0 ) );
	g_pDevice->SetIndices( IB );
	// set instance buffer
	g_pDevice->SetStreamSourceFreq( 1, D3DSTREAMSOURCE_INSTANCEDATA | 1ul );
	g_pDevice->SetStreamSource( 1, m_pInstanceBuffer, 0, D3DXGetDeclVertexSize( s_VertexDecl, 1 ) );

	
	g_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE );
	g_pDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
	g_pDevice->SetRenderState(D3DRS_LIGHTING, false);
		
	// use alpha from texture
	g_pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	g_pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

	g_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
	g_pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
    g_pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}

void CParticleCluster::Render() const {
	
	g_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, 4, 0, 2 );
}

void CParticleCluster::PostRender() const {
	
	g_pDevice->SetRenderState(D3DRS_CULLMODE,			D3DCULL_CCW );
	g_pDevice->SetRenderState(D3DRS_ZENABLE,			TRUE);
	g_pDevice->SetRenderState(D3DRS_LIGHTING,			TRUE);
	g_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,	TRUE);

	g_pDevice->SetStreamSourceFreq( 0, 1 );
	g_pDevice->SetStreamSourceFreq( 1, 1 );
}



struct Vertex {
	D3DXVECTOR3		position;
	FLOAT			u, v;
	static const DWORD FVF = D3DFVF_XYZ | D3DFVF_TEX1;
};

ID3DXMesh* CParticleCluster::_CreateBillboardMesh() {
	
	ID3DXMesh* pMesh;
	D3DXCreateMeshFVF( 2, 4, D3DXMESH_MANAGED, Vertex::FVF, *g_pDevice, &pMesh );

	Vertex* VertexBuffer;
	const float size = 0.5f;
	pMesh->LockVertexBuffer( 0, (void**)&VertexBuffer );
		VertexBuffer[0].position = D3DXVECTOR3( size, size, 0 );
		VertexBuffer[1].position = D3DXVECTOR3( -size, size, 0 );
		VertexBuffer[2].position = D3DXVECTOR3( -size, -size, 0 );
		VertexBuffer[3].position = D3DXVECTOR3( size, -size, 0 );
		VertexBuffer[0].u = 1;		VertexBuffer[0].v = 0;
		VertexBuffer[1].u = 0;		VertexBuffer[1].v = 0;
		VertexBuffer[2].u = 0;		VertexBuffer[2].v = 1;
		VertexBuffer[3].u = 1;		VertexBuffer[3].v = 1;
	pMesh->UnlockVertexBuffer();
	VertexBuffer = 0;

	WORD* IndexBuffer;
	pMesh->LockIndexBuffer( 0, (void**)&IndexBuffer );
		IndexBuffer[0] = 0;
		IndexBuffer[1] = 2;
		IndexBuffer[2] = 1;

		IndexBuffer[3] = 0;
		IndexBuffer[4] = 3;
		IndexBuffer[5] = 2;
	pMesh->UnlockIndexBuffer();
	IndexBuffer = 0;

	DWORD* AttributeBuffer;
	pMesh->LockAttributeBuffer( 0, &AttributeBuffer );
		AttributeBuffer[0] = AttributeBuffer[1] = 0;
	pMesh->UnlockAttributeBuffer();
	AttributeBuffer = 0;

	return pMesh;
}